package miao

import (
	"context"
	"encoding/json"
	"github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/mem"
	log "github.com/sirupsen/logrus"
	"miao-va-host/global"
	"miao-va-host/model"
	"strconv"
	"time"
)

func (c *Client) handsUp() (err error) {
	var (
		plug model.PluginInstruct
		resp model.ClientResponse
	)
	switch global.ClientType {
	case global.ClientType:
		plug = model.PluginInstruct{
			Cmd:  "hands_up.exe",
			Args: []string{"-p=C:\\miaoClient\\miaoClient.exe"},
		}
		if resp, err = execPlugin(c.ClientId, time.Now().Unix(), plug); err != nil {
			log.Error(err)
			return err
		}
		resp.Code = global.WhoAmiForIdp
	default:
		log.Error("unknown client")
	}
	err = c.WriteMessage(resp)
	return err
}

func (c *Client) setDeviceId(inst model.ClientInstruct) error {
	id, err := strconv.ParseInt(inst.Body, 10, 64)
	if err != nil {
		return err
	}
	c.ClientId = id
	return nil
}

func (c *Client) startGuarder(inst model.ClientInstruct) {
	var plug model.PluginInstruct
	var err error
	if err = json.Unmarshal([]byte(inst.Body), &plug); err != nil {
		log.Error(err)
		return
	}
	if c.pipCancel != nil {
		c.pipCancel()
		c.pipCancel = nil
	}
	ctx, cancel := context.WithCancel(context.Background())
	c.pipCancel = cancel
	conf := model.PluginPipeInstruct{
		Cmd:       plug.Cmd,
		Args:      plug.Args,
		User:      plug.User,
		Env:       plug.Env,
		OutChan:   c.pipOutChan,
		CloseChan: c.pipErrChan,
		Cancel:    ctx,
	}
	go func() {
		for {
			select {
			case msg := <-c.pipOutChan:
				log.Debug("读取到通道数据：", msg)
				resp := model.ClientResponse{
					DeviceId:   c.ClientId,
					ClientType: global.ClientType,
					ClientTime: time.Now().Unix(),
					Code:       global.GuarderPipMsg,
					Timely:     global.DataTypeForTimely,
					Content: model.PluginResult{
						Plugin:           plug.Cmd,
						Args:             plug.Args,
						InstructSendTime: inst.ServeTime,
						ElapsedTime:      0,
						Result:           msg,
					},
				}
				if err = c.WriteMessage(resp); err != nil {
					log.Error(err)
					// 将数据写入本地sqlite
					break
				}
			case ermsg := <-c.pipErrChan:
				log.Error("err:", ermsg)
				// 不能return，因为 pipErrChan 通道读取到错误时没有关闭插件的携程，如果这里return就不能继续读取插件内容了
				//return
			}
		}

	}()
	go func() {
		err = Plugin.RunPipe(conf)
		if err != nil {
			log.Error(err)
		}
	}()

}

func (c *Client) stopGuarder(inst model.ClientInstruct) {
	if c.pipCancel != nil {
		c.pipCancel()
		c.pipCancel = nil
	}
}

func (c *Client) execPlugin(inst model.ClientInstruct) (err error) {
	var plug model.PluginInstruct
	if err = json.Unmarshal([]byte(inst.Body), &plug); err != nil {
		log.Error(err)
		return
	}
	var resp model.ClientResponse
	if resp, err = execPlugin(c.ClientId, inst.ServeTime, plug); err != nil {
		log.Error(err)
		return err
	}
	if err = c.WriteMessage(resp); err != nil {
		log.Error(err)
		return err
	}
	return err
}

func execPlugin(clientId int64, sendTime int64, plug model.PluginInstruct) (resp model.ClientResponse, err error) {
	var stdout string
	t1 := time.Now().Unix()
	stdout, err = Plugin.Run(plug)
	if err != nil {
		return
	}
	var t2 = time.Now().Unix()
	resp = model.ClientResponse{
		DeviceId:   clientId,
		ClientType: global.ClientType,
		ClientTime: time.Now().Unix(),
		Code:       global.ExecutePlugin,
		Timely:     1,
		Content: model.PluginResult{
			Plugin:           plug.Cmd,
			Args:             plug.Args,
			InstructSendTime: sendTime,
			ElapsedTime:      t2 - t1,
			Result:           stdout,
		},
	}
	return
}

// getLoad 获取当前时间节点上的cpu和内存负载
func getLoad() (cpuPre, memPre float64) {
	var (
		fs  []float64
		err error
	)
	if fs, err = cpu.Percent(0, false); err != nil {
		return 0, 0
	}
	var total float64
	for i := range fs {
		total += fs[i]
	}
	cpuPre = total / float64(len(fs))
	if cpuPre < 1.0 {
		cpuPre = 1.0
	}
	cpuPre = cpuPre / 100.0
	vs, err := mem.VirtualMemory()
	if err != nil {
		return
	}
	memPre = vs.UsedPercent
	if memPre < 1.0 {
		memPre = 1.0
	}
	memPre = memPre / 100.0
	return
}
